I never forget a face, but in your case I'll make an exception.
Groucho (Julius Henry) Marx

No rule is so general, which admits not some exception.
Robert Burton, The Anatomy of Melancholy

It is common sense to take a method and try it. If it fails, admit it frankly and try another. But above all, try something.
Franklin Delano Roosevelt

O! throw away the worser part of it, And live the purer with the other half.
William Shakespeare

If they're running and they don't look where they're going I have to come out from somewhere and catch them.
Jerome David Salinger

And oftentimes excusing of a fault Doth make the fault the worse by the excuse.
William Shakespeare

To err is human, to forgive divine.
Alexander Pope, An Essay on Criticism

Answer 13.1
Insufficient memory to satisfy a new request, array subscript out of bounds, arithmetic overflow, division by zero, invalid function parameters.

Answer 13.2
a) Exception handling is designed to handle infrequently occurring situations that often result in program termination, so compiler writers are not required to implement exception handling to perform optimally.
b) Flow of control with conventional control structures is generally clearer and more efficient than with exceptions.
c) Problems can occur because the stack is unwound when an exception occurs and resources allocated prior to the exception may not be freed.
d) The "additional" exceptions can get in the way of genuine error-type exceptions. It becomes more difficult for the programmer to keep track of the larger number of exception cases. What does a catch(...) really catch?
Answer 13.3
It is unlikely that a library function will perform error processing that will meet the unique needs of all users.


Answer 13.4
An aborting program could leave a resource in a state in which other programs would not be able to acquire the resource.


Answer 13.5
The exception handlers (in the catch blocks) for that try block are skipped and the program resumes execution after the last catch block.


Answer 13.6
An exception thrown outside a try block causes a call to terminate.


Answer 13.7
The form catch(...) catches any type of error thrown in a try block. An advantage is that no thrown error can slip by. A disadvantage is that the catch has no parameter so it cannot reference information in the thrown object and cannot know the cause of the error.


Answer 13.8
This causes the search for a match to continue in the next enclosing try block. As this process continues, it may eventually be determined that there is no handler in the program that matches the type of the thrown object; in this case terminate is called, which by default calls abort. An alternate terminate function can be provided as an argument to set_terminate.


Answer 13.9
The first matching exception handler after the try block is executed.


Answer 13.10
This is a nice way to catch related types of exceptions.


Answer 13.11
Provide a single exception class and catch handler for a group of exceptions. As each exception occurs, the exception object can be created with different private data. The catch handler can examine this private data to distinguish the type of the exception.


Answer 13.12

void *.



Answer 13.13
A handler requiring standard conversions may appear before one with a precise match.


Answer 13.14
No, but it does terminate the block in which the exception is thrown.


Answer 13.15
The exception will be processed by a catch handler (if one exists) associated with the try block (if one exists) enclosing the catch handler that caused the exception.


Answer 13.16
It rethrows the exception.


Answer 13.17
Provide an exception specification listing the exception types that can be thrown from the function.


Answer 13.18
Function unexpected is called.


Answer 13.19
Through the process of stack unwinding, destructors are called for each of these objects.


Answer 13.21
If there is no information in the object that is required in the handler, a parameter name is not required in the handler.

Answer 13.27
Create a base class for all related exceptions. In the base class, derive all the related exception classes. Once the exception class hierarchy is created, exceptions from the hierarchy can be caught as the base class exception type or as one of the derived class exception types.

Answer 13.29
The solution to this exercise can be found on your Cyber Classroom CD. Copy the file cpphtp2/answers/P13_29.zip to your hard drive and unzip the program code.

Answer 13.31
The solution to this exercise can be found on your Cyber Classroom CD. Copy the file cpphtp2/answers/P13_31.zip to your hard drive and unzip the program code.

Answer 13.34
The solution to this exercise can be found on your Cyber Classroom CD. Copy the file cpphtp2/answers/P13_34.zip to your hard drive and unzip the program code.

Answer 13.40
The solution to this exercise can be found on your Cyber Classroom CD. Copy the file cpphtp2/answers/P13_40.zip to your hard drive and unzip the program code.

Answer 13.42
The solution to this exercise can be found on your Cyber Classroom CD. Copy the file cpphtp2/answers/P13_42.zip to your hard drive and unzip the program code.

13 Exception Handling
13.1Introduction
13.2When Exception Handling Should Be Used
13.3Other Error-Handling Techniques
13.4Basics of C++ Exception Handling: try, throw, catch
13.5A Simple Exception-Handling Example: Divide by Zero
13.6Throwing an Exception
13.7Catching an Exception
13.8Rethrowing an Exception
13.9Exception Specifications
13.10Processing Unexpected Exceptions
13.11Stack Unwinding
13.12Constructors, Destructors and Exception Handling
13.13Exceptions and Inheritance
13.14Processing new Failures
13.15Class auto_ptr and Dynamic Memory Allocation
13.16Standard Library Exception Hierarchy
13.17Summary
Terminology
Figures
13.1 Introduction
In this chapter, we introduce exception handling. The extensibility of C++ can increase substantially the number and kinds of errors that can occur. The features presented here enable programmers to write clearer, more robust, more fault-tolerant programs. Recent systems developed with these and/or similar techniques have reported positive results. We also mention when exception handling should not be used.
The style and details of exception handling presented in this chapter are based on the work of Andrew Koenig and Bjarne Stroustrup as presented in their paper,
"Exception Handling for C++ (revised)," published in the Proceedings of the USENIX C++ Conference held in San Francisco in April, 1990.
Error-handling code varies in nature and amount among software systems depending on the application and whether or not the software is a product for release. Commercial products tend to contain far more error- handling code than "casual" software.
There are many popular means of dealing with errors. Most commonly, error-handling code is interspersed throughout a system's code. Errors are dealt with at the places in the code where the errors can occur. The advantage to this approach is that a programmer reading
code can see the error processing in the immediate vicinity of the code and determine if the proper error checking has been implemented.
The problem with this scheme is that the code in a sense becomes "polluted" with the error processing. It becomes more difficult for a programmer concerned with the application itself to read the code and determine if the code is functioning correctly. This makes it more difficult to understand and to maintain the code.
Some common examples of exceptions are failure of new to obtain a requested amount of memory, an out-
of-bounds array subscript, arithmetic overflow, division by zero, and invalid function parameters.
C++'s new exception-handling features enable the programmer to remove the error-handling code from the "main line" of a program's execution. This improves program readability and modifiability.
With the C++ style of exception handling it is possible to catch all kinds of exceptions, to catch all exceptions of a certain type or to catch all exceptions of related types. This makes programs more robust by reducing the likelihood that errors will not be caught by a program. Exception handling is provided to enable programs to catch and handle errors rather than letting
them occur and suffering the consequences. If the programmer does not provide a means of handling a fatal error, the program terminates.
Exception handling is designed for dealing with synchronous errors such as an attempt to divide by zero (that occurs as the program executes the divide instruction). With exception handling, before the program executes the division, it checks the denominator and "throws" (issues) an exception if the denominator is zero.
Exception handling is not designed to deal with asynchronous situations such as disk I/O completions, network message arrivals, mouse clicks, and the like;
these are best handled through other means, such as interrupt processing.
Exception handling is used in situations in which the system can recover from the error causing the exception. The recovery procedure is called an exception handler.
Exception handling is typically used in situations in which the error will be dealt with by a different part of the program (i.e., a different scope) from that which detected the error. A program that carries on an interactive dialog with a user should not use exceptions to process input errors.
Exception handling is especially appropriate for situations in which the program will not be able to recover, but needs to provide orderly cleanup, then shut down "gracefully."
There is another reason to avoid using exception handling techniques for conventional program control. Exception handling is designed for error processing which is an infrequent activity that is often used because a program is about to terminate. Given this, it is not required that C++ compiler writers implement exception handling for the kind of optimal performance that might be expected of regular application code.
Exception handling helps improve a program's fault tolerance. It becomes "more pleasant" to write error- processing code, so programmers are more likely to provide it. It also becomes possible to catch exceptions in a variety of ways such as by type, or even to specify that exceptions of any type are to be caught.
The majority of programs written today support only a single thread of execution. Multithreading is receiving great attention in recent operating systems like Windows NT, OS/2, and various versions of UNIX. The techniques discussed in this chapter apply even for multithreaded programs, although we do not discuss multithreaded programs specifically.
We will show how to deal with "uncaught" exceptions. We will consider how unexpected exceptions are handled. We will show how related exceptions can be represented by exception classes derived from a common base exception class.
The exception-handling features of C++ are becoming widely used as a result of the ANSI C++ standardization effort. Standardization is especially important on large software projects where dozens or even hundreds of people work on separate components of a system and these components need to interact for the overall system to function properly.
Exception handling can be viewed as another means of returning control from a function or exiting a block of code. Normally, when an exception occurs, it will be handled by a caller of the function generating the exception, by a caller of that caller, or however far back in the call chain it becomes necessary to go to find a handler for that exception.
Select the true statement(s).
Exception handling is designed for asynchronous errors.
Exception handling helps improve a program's fault tolerance.
If the programmer does not provide the means of handling an exception, the program terminates.
13.2 When Exception Handling Should Be Used
Exception handling should be used to process only exceptional situations, despite the fact that there is nothing to prevent the programmer from using exceptions as an alternate for program control; to process exceptions for program components that are not geared to handling those exceptions directly; to process exceptions from software components such as functions, libraries, and classes that are likely to be widely used, and where it does not make sense for those components to handle their own exceptions; on large
projects to handle error processing in a uniform manner project wide.
13.3 Other Error-Handling Techniques
We have presented a variety of ways of dealing with exceptional situations prior to this chapter. The following summarizes these and other useful techniques.
* Use assert to test for coding and design errors. If an assertion is false, the program terminates and the code must be corrected. This is useful at debugging time. * Simply ignore the exceptions. This would be devastating for software products released to the general public, or for special-purpose software needed for mission- critical situations. But for your own software developed
* for your own purposes, it is quite common to ignore many kinds of errors. * Abort the program. This, of course, prevents a program from running to completion and producing incorrect results. Actually, for many types of errors this is a good strategy, especially for nonfatal errors that enable a program to run to completion, perhaps misleading the programmer to think that the program functioned correctly. Here, too, such a strategy is inappropriate for mission-critical applications. Resource issues are also important here. If a program obtains a resource, the program should normally return that resource before program termination. * Set some error indicator. The problem with this is that programs may not check these error indicators at all points at which the errors could be troublesome. * Test for the error condition, issue an error message, and call exit to pass an appropriate error code to the program's environment. * setjump and longjump. These capabilities, available through <setjmp.h> enable the programmer to specify an immediate jump out of deeply nested function calls back to an error handler. Without setjump/longjump, a program must execute several returns to get out of the deeply nested function calls. These could certainly be used to jump to some error handler. But they are dan * gerous in C++ because they unwind the stack without calling destructors for automatic objects. This can lead to serious problems. * Certain specific kinds of errors have dedicated capabilities for handling them. For example, when new fails to allocate memory, it can cause a new_handler function to execute to deal with the error. This function can be varied by supplying a function name as the argument to set_new_handler. We discuss function set_new_handler in detail in Section 13.14.
13.4 Basics of C++ Exception Handling: try, throw, catch
C++ exception handling is geared to situations in which the function that detects an error is unable to deal with it. Such a function will throw an exception. There is no guarantee that there will be "anything out there"--i.e., an exception handler specifically geared to processing that kind of exception. If there is, the exception will be caught and handled. If there is no exception handler for that particular kind of exception, the program terminates.
The programmer encloses in a try block the code that may generate an error that will produce an exception. The try block is followed by one or more catch blocks. Each catch block specifies the type of exception it can catch and handle. Each catch block contains an exception handler. If the exception matches the type of the parameter in one of the catch blocks, the code for that catch block is executed. Otherwise, the exception mechanism looks for an exception handler in any enclosing try block. If no handler is found, function terminate is called, which by default calls function abort.
Program control on a thrown exception leaves the try block and searches the catch blocks in order for an appropriate handler (we will soon discuss what makes a handler "appropriate"). If no exceptions are thrown in the try block, the exception handlers for that block are skipped and the program resumes execution after the last catch block.
We can specify the exceptions a function throws. As an option, we can specify that a function shall not throw any exceptions at all.
The exception is thrown in a try block in the function, or the exception is thrown from a function called directly or indirectly from the try block. The point at
which the throw is executed is called the throw point. This term is also used to describe the throw expression itself. Once an exception is thrown, control cannot return to the throw point.
When an exception occurs, it is possible to communicate information to the exception handler from the point of the exception. That information is the type of the thrown object itself or information placed into the thrown object.
The thrown object is typically a character string (for an error message) or a class object. The thrown object conveys information to the exception handler that will process that exception.
Select the true statement(s).
A try block can be followed with at most two catch blocks.
Function terminate calls function abort.
The point at which throw is executed is called the throw point.
Once an exception is thrown, control cannot continue from the throw point.
The thrown object conveys information to the exception handler that will process that exception.
13.5 A Simple Exception-Handling Example: Divide by Zero
Now let us consider a simple example of exception handling. The program of Fig. 13.1 uses try, throw and catch to detect a division by zero, indicate a divide-by- zero exception and handle a divide-by-zero exception.
The first output window shows a successful execution. In the second, a zero denominator is entered and the program detects the error and issues an error message.
Now consider the driver program in main. The program prompts for, and inputs, two integers. Note the "localized" declaration of number1 and number2.
Next, the program proceeds with a try block which wraps the code that may throw an exception. Note that the actual division that may cause the error is not explicitly listed inside the try block. Rather, the call to function quotient invokes the code that attempts the actual division. Function quotient actually throws the divide-by-zero exception object as we will see momentarily. In general, errors may surface through explicitly mentioned code in the try block, through calls to a function, or even through deeply nested function calls initiated by code in the try block.
The try block is immediately followed by a catch block containing the exception handler for the divide-by-zero
error. In general, when an exception is thrown within a try block, the exception is caught by a catch block that specifies the appropriate type that matches the thrown exception. In Fig. 13.1, the catch block specifies that it will catch exception objects of type DivideByZeroException; this type matches the type of the object thrown in function quotient. The body of this exception handler issues an error message and returns, in this case with 1 indicating termination because of an error. Exception handlers can be much more elaborate than this.
If, when executed, the code in a try block does not throw an exception, then all the catch handlers
immediately following the try block are skipped and execution resumes with the first line of code after the catch handlers; in Fig. 13.1 a return statement is executed that returns 0, indicating normal termination.
Now let us examine the definitions of class DivideByZeroException and function quotient. In function quotient, when the if statement determines that the denominator is zero, the body of the if statement issues a throw statement which specifies the name of the constructor for the exception object. This causes an object of class DivideByZeroException to be created. This object will be caught by the catch statement (specifying type DivideByZeroException)
after the try block. The constructor for class DivideByZeroException simply points data member message at the string "Divide by zero". The thrown object is received in the parameter specified in the catch handler (in this case, parameter error), and the message is printed there through a call to public access function printMessage.
13.6 Throwing an Exception
The throw keyword is used to indicate that an exception has occurred. This is called throwing an exception. A throw normally specifies one operand (a special case we will discuss specifies no operands). The operand of a throw can be of any type. If the operand is an object, we call it an exception object. A conditional expression can be thrown instead of an object. It is possible to throw objects not intended for error handling.
Where is an exception caught? Upon being thrown, the exception will be caught by the closest exception
handler (for the try block from which the exception was thrown) specifying an appropriate type. The exception handlers for a try block are listed immediately following the try block.
As part of throwing an exception, a temporary copy of the throw operand is created and initialized. This temporary object then initializes the parameter in the exception handler. The temporary object is destroyed when the exception handler completes execution and exits.
When an exception is thrown, control exits the current try block and proceeds to an appropriate catch handler (if one exists) after that try block. It is possible that the
throw point could be in a deeply nested scope within a try block; control will still proceed to the catch handler. It is also possible that the throw point could be in a deeply nested function call; still, control will proceed to the catch handler.
A try block may appear to contain no error checking and include no throw statements, but code referenced in the try block could certainly cause error-checking code in constructors to execute. Code in a try block could perform array subscripting on an array class object whose operator[] member function could be overloaded to throw an exception on a subscript-out- of-range error. Any function call can invoke code that
might throw an exception or call another function that throws an exception.
Although an exception can terminate program execution, it is not required to terminate program execution. However, an exception does terminate the block in which the exception occurred.
Select the true statement(s).
As part of throwing an exception, a temporary copy of the throw operand is created and initialized.
The operand of throw can only be an object.
The throw keyword is used to indicate that an exception has occured.
13.7 Catching an Exception
Exception handlers are contained in catch blocks. Each catch block starts with the keyword catch followed by parentheses containing a type (indicating the type of exception this catch block handles) and an optional parameter name. This is followed by braces delineating the exception-handling code. When an exception is caught, the code in the catch block is executed.
The catch handler defines its own scope. A catch specifies in parentheses the type of the object to be caught. The parameter in a catch handler can be named or unnamed. If the parameter is named, the parameter
can be referenced in the handler. If the parameter is unnamed, i.e., only a type is listed for purposes of matching with the thrown object type, then information is not conveyed from the throw point to the handler; only control passes from the throw point to the handler. For many exceptions, this is acceptable.
An exception whose thrown object's type matches the type of the argument in the catch header causes the catch block, i.e., the exception handler for exceptions of that type, to execute.
The catch handler that catches an exception is the first one listed after the currently active try block that
matches the type of the thrown object. The matching rules are discussed shortly.
An exception that is not caught causes a call to terminate which by default terminates a program by calling abort. It is possible to specify customized behavior by designating another function to be executed by providing that function's name as the argument in a set_terminate function call.
A catch followed by parentheses enclosing an ellipsis

catch( ... )

means to catch all exceptions.
It is possible that no handler will match a particular thrown object. This causes the search for a match to
continue in the next enclosing try block. As this process continues, it may eventually be determined that there is no handler in the program that matches the type of the thrown object; in this case function terminate is called, which by default calls function abort.
The exception handlers are searched in order for an appropriate match. The first handler that yields a match is executed. When that handler finishes executing, control resumes with the first statement after the last catch block, i.e., the first statement after the last exception handler for that try block.
It is possible that several exception handlers will provide an acceptable match to the type of the
exception that was thrown. In this case, the first exception handler that matches the exception type is executed. If several handlers match, and if each of these handles the exception differently, then the order of the handlers will affect the manner in which the exception is handled.
It is possible that several catch handlers could contain a class type that would match the type of a particular thrown object. This can happen for several reasons. First, there can be a "catch-all" handler catch(...) that will catch any exception. Second, because of inheritance hierarchies, it is possible that a derived- class object can be caught either by a handler specifying
the derived-class type, or by handlers specifying the types of any base classes of that derived class.
Sometimes a program may process many closely related types of exceptions. Instead of providing separate exception classes and catch handlers for each, a programmer can provide a single exception class and catch handler for a group of exceptions. As each exception occurs, the exception object can be created with different private data. The catch handler can examine this private data to distinguish the type of the exception.
When does a match occur? The type of the catch handler parameter matches the type of the thrown object if
* they are indeed of the same type. * the catch handler parameter type is a public base class of the class of the thrown object. * the handler parameter is of a base-class pointer or reference type and the thrown object is of a derived- class pointer or reference type. * the catch handler is of the form catch( ... ). An exact type match is required,. No promotions or conversions are performed when looking for a handler except for derived-class-to-base-class conversions.
It is possible to throw const objects. In this case, the catch handler argument type must also be declared const.
By default, if no handler is found for an exception, the program terminates. Although this may seem like the right thing to do, it is not what programmers are necessarily used to doing. Rather, errors often simply happen and then program execution continues, possibly only "hobbling" along.
A try block followed by several catches resembles a switch statement. It is not necessary to use break to exit an exception handler in a manner that skips over the remaining exception handlers. Each catch block
defines a distinct scope whereas all the cases in a switch statement are contained within the scope of the switch.
An exception handler cannot access automatic objects defined within its try block because when an exception occurs the try block terminates and all the automatic objects inside the try block are destroyed before the handler begins executing.
What happens when an exception occurs in an exception handler? The original exception that was caught is officially handled when the exception handler begins executing. So exceptions occurring in an
exception handler need to be processed outside the try block in which the original exception was thrown.
Exception handlers can be written in a variety of ways. They could take a closer look at an error and decide to call terminate. They could rethrow an exception (Section 13.8). They could convert one type of exception into another by throwing a different exception. They could perform any necessary recovery and resume execution after the last exception handler. They could look at the situation causing the error, remove the cause of the error and retry by calling the original function that caused an exception (this would
not create infinite recursion). They could return some status value to their environment, etc.
When a try block does not throw any exceptions and the try block completes normal execution, control passes to the first statement after the last catch handler following the try block.
It is not possible to return to the throw point by issuing a return statement in a catch handler. Such a return simply returns to the function that called the function containing the catch block.
When an exception is caught, it is possible that resources may have been allocated but not yet released in the try block. The catch handler, if possible, should
release these resources. For example, the catch handler should delete space allocated by new and should close any files opened in the try block that threw the exception.
A catch block can process the error in a manner that enables the program to continue executing correctly. Or the catch block can terminate the program.
A catch handler itself can discover an error and throw an exception. Such an exception will not be processed by catch handlers associated with the same try block as the catch handler throwing the exception. Rather the thrown exception will be caught, if possible, by a catch handler associated with the next outer try block.
Select the true statement(s).
An ellipsis inside a catch indicates that any exceptions can be caught by that exception handler.
A catch block cannot define its own scope.
After a catch block finishes executing, the statement after the last catch block of the try/catch sequence executes.
A catch block specifying a base class type will not catch a derived class exception object.
13.8 Rethrowing an Exception
It is possible that the handler that catches an exception may decide that it cannot process the exception or it may simply want to release resources before letting someone else handle it. In this case, the handler can simply rethrow the exception with the statement

throw;

Such a throw with no arguments rethrows the exception. If no exception was thrown to begin with, then the rethrow causes a call to terminate.
Even if a handler can process an exception, and regardless of whether it does any processing on that
exception, the handler can still rethrow the exception for further processing outside the handler.
A rethrown exception is detected by the next enclosing try block and is handled by an exception handler listed after that enclosing try block.
The program of Fig. 13.2 demonstrates rethrowing an exception. In the try block at line 26 of main, function throwException is called. In the try block of function throwException, the throw statement at line 13 throws an instance of standard library class exception (defined in header file <exception>). This exception is caught immediately in the catch handler at line 15 which prints an error message then rethrows the
exception. This terminates function throwException and returns control to the try/catch block in main. The exception is caught again at line 30 and an error message is printed.
Select the true statement(s).
The statement throw; in a catch handler causes an exception to be rethrown.
An exception can only be rethrown once.
The C++ standard library provides class exception.
13.9 Exception Specifications
An exception specification enables a list of exceptions that can be thrown by a function to be specified.

int g( float h ) throw ( a, b, c )

{

// function body

}

It is possible to restrict the exception types thrown from a function. The exception types are specified in the function declaration as an exception specification (also called a throw list). The exception specification lists the exceptions that may be thrown. A function may throw the indicated exceptions or derived types.
Despite this supposed guarantee that other exception types will not be thrown, it is possible to do so. If an exception not listed in the exception specification is thrown, function unexpected is called.
Placing throw() (i.e., an empty exception specification) after a function's parameter list states that the function will not throw any exceptions. Such a function could, in fact, throw an exception; this, too, would generate a call to unexpected.
A function with no exception specification can throw any exception.

void g();      // this function can throw any exception

The meaning of the unexpected function can be redefined by calling function set_unexpected.
One interesting aspect of exception handling is that the compiler will not consider it a syntax error if a function contains a throw expression for an exception not listed in the function's exception specification. The function must attempt to throw that exception at execution time before the error will be caught.
If a function throws an exception of a particular class type, that function can also throw exceptions of all classes derived from that class with public inheritance.
Select the true statement(s).
Placing throw() after a function's parameter list states that a function will not throw any exceptions.
A function's exception specification list specifies the exceptions a function can throw.
A function can throw any number of exceptions at the same time.
If an exception is thrown that is not listed in the throw list, a call to unexpected occurs.
13.10 Processing Unexpected Exceptions
Function unexpected calls the function specified with the set_unexpected function. If no function has been specified in this manner, terminate is called by default.
Function terminate can be called explicitly, if a thrown exception cannot be caught, if the stack is corrupted during exception handling, as the default action on a call to unexpected, and during stack unwinding initiated by an exception, an attempt by a destructor to throw an exception causes terminate to be called.
Function set_terminate can specify the function that will be called when terminate is called. Otherwise, terminate calls abort.
Prototypes for functions set_terminate and set_unexpected are found in header files <terminate.h> and <unexpected.h>, respectively.
Function set_terminate and function set_unexpected each return a pointer to the last function called by terminate and unexpected. This enables the programmer to save the function pointer so it can be restored later.
Functions set_terminate and set_unexpected take pointers to functions as arguments. Each argument must
point to a function with void return type and no arguments.
If the last action of a user-defined termination function is not to exit a program, function abort will automatically be called to end program execution after the other statements of the user-defined termination function are executed.
13.11 Stack Unwinding
When an exception is thrown but not caught in a particular scope, the function-call stack is unwound and an attempt is made to catch the exception in the next outer try/catch block. Unwinding the function-call stack means that the function in which the exception was not caught terminates, all local variables in that function are destroyed and control returns to the point at which that function was called. If that point in the program is in a try block, an attempt is made to catch the exception. If that point in the program is not in a try block or the exception is not caught, stack unwinding
occurs again. As mentioned in the previous section, if the exception is not caught in the program, function terminate is called to terminate the program. The program of Fig. 13.3 demonstrates stack unwinding.
In main, the try block at line 25 calls function1. Next, function1 (defined at line 18) calls function2. Then, function2 (defined at line 13) calls function3. Line 10 of function3 throws an exception object. Because line 10 is not in a try block, stack unwinding occurs-- function3 terminates and control returns to function2 at line 15. Because line 15 is not in a try block, stack unwinding occurs again--function2 terminates and control returns to function1 at line 20. Because line 20
is not in a try block, stack unwinding occurs one more time--function1 terminates and control returns to main at line 26. Because line 26 is in a try block, the exception can be caught and processed in the first matching catch handler after the try block (at line 28).
13.12 Constructors, Destructors and Exception Handling
First, let us deal with an issue we have mentioned, but that has yet to be satisfactorily resolved. What happens when an error is detected in a constructor? For example, how should a String constructor respond when new fails and indicates that it was unable to obtain the space needed to hold the String's internal representation? The problem is that a constructor cannot return a value, so how do we let the outside world know that the object has not been properly constructed? One scheme was simply to return the improperly constructed object and
hope that anyone using the object would make appropriate tests to determine that the object was in fact bad. Another scheme is to set some variable outside the constructor. A thrown exception passes to the outside world the information about the failed constructor and the responsibility to deal with the failure.
To catch an exception, the exception handler must have access to a copy constructor for the thrown object (default memberwise copy is also valid).
Exceptions thrown in constructors cause destructors to be called for any objects built as part of the object being constructed before the exception is thrown.
Destructors are called for every automatic object constructed in a try block before an exception is thrown. An exception is handled at the moment the handler begins executing; stack unwinding is guaranteed to have been completed at that point. If a destructor invoked as a result of stack unwinding throws an exception, terminate is called.
If an object has member objects and if an exception is thrown before the outer object is fully constructed, then destructors will be executed for the member objects that have been constructed prior to the occurrence of the exception.
If an array of objects has been partially constructed when an exception occurs, only the destructors for the constructed array elements will be called.
An exception could preclude the operation of code that would normally release a resource, thus causing a resource leak. One technique to resolve this problem is to initialize a local object when the resource is acquired. When an exception occurs, the destructor will be invoked and can free the resource.
It is possible to catch exceptions thrown from destructors by enclosing the function that calls the destructor in a try block and provide a catch handler with the proper type. The thrown object's destructor
executes after an exception handler completes execution.
Select the true statement(s).
Destructors are not called for every automatic object constructed in a try block before the exception is thrown.
Exceptions thrown in constructors cause destructors to be called for any object built as part of the constructor call before the exception is thrown.
13.13 Exceptions and Inheritance
Various exception classes can be derived from a common base class. If a catch catches a pointer or reference to an exception object of a base-class type, it can also catch a pointer or reference to all objects of classes derived from that base class. This can allow for polymorphic processing of related errors.
13.14 Processing new Failures
There are several methods of dealing with new failures. To this point, we have used macro assert to test the value returned from new. If that value is 0, the assert macro terminates the program. This is not a robust mechanism for dealing with new failures--it does not allow us to recover from the failure in any way. The ANSI/ISO C++ draft standard specifies that when new fails, it throws a bad_alloc exception (defined in header file <new>). However, many compilers are not current with the draft standard and still use the version of new that returns 0 on failure. In this section we
present three examples of new failing. The first example was compiled with Microsoft's Visual C++ 5.0 which at the time of this publication still used the version of new that returns 0 when new fails. The second and third examples were compiled with the C++ compiler in Metrowerks CodeWarrior Professional Release 1 which uses the version of new that throws a bad_alloc exception when new fails. We tested the three programs on separate computers running Windows 95. Each machine had different amounts of memory and hard disk space.
Figure 13.4 demonstrates new returning 0 on failure to allocate the requested amount of memory. The for
structure at line 10 is supposed to loop 10 times and allocate an array of 5,000,000 double values (i.e., 40,000,000 bytes because a double is normally 8 bytes) each time through the loop. The if structure at line 13 tests the result of each new operation to determine if the memory was allocated. If new fails and returns 0, the message "Memory allocation failed\n" is printed and the loop terminates.
The output shows that only two iterations of the loop were performed before new failed and the loop terminated. The output on your system may differ based on the physical memory and disk space you have available for virtual memory.
Figure 13.5 demonstrates new throwing bad_alloc when it fails to allocate the requested memory. The for structure at line 12 inside the try block is supposed to loop 10 times and on each pass allocate an array of 5,000,000 double values (i.e., 40,000,000 bytes because a double is normally 8 bytes). If new fails and throws a bad_alloc exception, the loop terminates and the program continues in the exception-handling flow of control at line 18 where the exception is caught and processed. The message "Exception occurred: " is printed followed by exception.what() which returns a string with an exception-specific message ("Allocation Failure" in the case of bad_alloc). The output shows
that only three iterations of the loop were performed before new failed and threw the bad_alloc exception. Your output may differ based on the physical memory and disk space available for virtual memory on your system.
Compilers vary in the way they support new failure handling. In general, many current and older C++ compilers return 0 by default when new fails. Some of these compilers support new throwing an exception if the header file <new> (or <new.h>) is included. Other compilers--such as CodeWarrior Professional Release 1--throw bad_alloc by default whether or not you include header file <new>. Read the documentation for
your compiler to determine your compiler's support for new failure handling.
The ANSI/ISO C++ draft standard specifies that standard-compliant compilers can still use a version of new that returns 0 when it fails. For this purpose, the header file <new> defines the type nothrow that is used as follows:

double *ptr = new( nothrow ) double[ 5000000 ];

The preceding statement indicates that the version of new that does not throw bad_alloc exceptions (i.e., nothrow) should be used to allocate an array of 5,000,000 double values.
There is an additional feature that can be used to perform handling of new failures. Function set_new_handler (prototyped in header file <new> or <new.h>) takes as its argument a function pointer for a function that takes no arguments and returns void. The function pointer is registered as the function to call when new fails. This provides the programmer with a uniform method of processing every new failure regardless of where the failure occurs in the program. Once a new handler is registered in the program with set_new_handler, new will not throw bad_alloc on failure.
Operator new is actually a loop that attempts to acquire memory. If the memory is allocated, new returns a pointer to that memory. If new fails to allocate memory and no new handler function has been registered with set_new_handler, new throws a bad_alloc exception. If new fails to allocate memory and a new handler function has been registered, the new handler function is called. The C++ draft standard specifies that the new handler function should perform one of the following tasks:
1. Make more memory available by deleting other dynamically allocated memory and return to the loop in operator new to attempt to allocate the memory again.
2. Throw an exception of type bad_alloc.
3. Call function abort or exit (both from header file <cstdlib> or <stdlib.h>) to terminate the program.
The program of Fig. 13.6 demonstrates set_new_handler. The function customNewHandler simply prints an error message and terminates the program with a call to abort. The output shows that only three iterations of the loop were performed before new failed and threw the bad_alloc exception. The output on your system may differ based on the physical
memory and disk space you have available for virtual memory.
Select the true statement(s).
ANSI/ISO draft standard C++ specifies that when new fails, it should throw a bad_alloc exception.
Once a new handler is registered in the program with set_new_handler, new will throw a bad_alloc on failure.
13.15 Class auto_ptr and Dynamic Memory Allocation
A common programming practice is to allocate dynamic memory (possibly an object) on the free store, assign the address of that memory to a pointer, use the pointer to manipulate the memory and deallocate the memory with delete when the memory is no longer needed. If an exception occurs after the memory has been allocated and before the delete statement is executed, a memory leak could occur. The ANSI/ISO C++ draft standard provides class template auto_ptr in header file <memory> to deal with this situation.
An object of class auto_ptr maintains a pointer to dynamically allocated memory. When an auto_ptr object goes out of scope, it performs a delete operation on its pointer data member. Class template auto_ptr provides operators * and -> so an auto_ptr object can be used like a regular pointer variable. Figure 13.7 demonstrates an auto_ptr object that points to an object of class Integer (defined at lines 818).
Line 25

auto_ptr< Integer > ptrToInteger( new Integer( 7 ) );

creates auto_ptr object ptrToInteger and initializes it with a pointer to a dynamically allocated Integer object containing the value 7.
Line 28

ptrToInteger->setInteger( 99 );  

uses the auto_ptr overloaded -> operator and the function call operator () to call function setInteger on the Integer object pointed to by ptrToInteger.
The call

( *ptrToInteger ).getInteger()

in line 30 uses the auto_ptr overloaded * operator to dereference ptrToInteger then uses the dot (.) operator
and the function call operator () to call function getInteger on the Integer object pointer to by ptrToInteger.
Because ptrToInteger is a local automatic variable in main, ptrToInteger is destroyed when main terminates. This forces a delete of the Integer object pointed to by ptrToInteger which, of course, forces a call to the Integer class destructor. Most importantly, this technique can prevent memory leaks.
Select the true statement(s).
An object of class auto_ptr maintains a pointer to dynamically allocated memory.
Class template auto_ptr provides operators -> and *.
When an auto_ptr goes out of scope, it will not perform a delete operation.auto_ptr
13.16 Standard Library Exception Hierarchy
Experience has shown that exceptions fall nicely into a number of categories. The C++ draft standard includes a hierarchy of exception classes. This hierarchy is headed by base class exception (defined in header file <exception>) which offers the service what() that is overridden in each derived class to issue an appropriate error message.
From base class exception, some of the immediate derived classes are runtime_error and logic_error (both defined in header <stdexcept>), each of which has several derived classes.
Also derived from exception are the exceptions thrown by C++ language features--for example, bad_alloc is thrown by new (Section 13.14), bad_cast is thrown by dynamic_cast (Chapter 21) and bad_typeid is thrown by typeid (Chapter 21). By including std::bad_exception in the throw list of a function, if an unexpected exception occurs, unexpected() will throw bad_exception instead of terminating (by default) or instead of calling another function specified with set_unexpected.
Class logic_error is the base class of several standard exception classes that indicate errors in program logic that can normally be prevented by writing proper code.
Descriptions of some of these classes follow. Class invalid_argument indicates that an invalid argument was passed to a function (proper coding can, of course, prevent invalid arguments from reaching a function). Class length_error indicates that a length larger than the maximum size allowed for the object being manipulated was used for that object (we throw length_errors in Chapter 19 when we deal with strings). Class out_of_range indicates that a value such as a subscript into an array or string was out of range.
Class runtime_error is the base class of several other standard exception classes that indicate errors in a
program that can only be detected at execution time. Class overflow_error indicates that an arithmetic overflow error occurred. Class underflow_error indicates that an arithmetic underflow error occurred.
13.17 Summary
* Some common examples of exceptions are an out-of- bounds array subscript, arithmetic overflow, division by zero, invalid function parameters and determining that there is insufficient memory to satisfy an allocation request by new. * The spirit behind exception handling is to enable programs to catch and handle errors rather than letting them occur and simply suffering the consequences. With exception handling, if the programmer does not provide a means of handling a fatal error, the program will terminate; nonfatal errors normally allow a pro
* gram to continue executing, but produce incorrect results. * Exception handling is designed for dealing with synchronous errors, i.e., errors that occur as the result of a program's execution. * Exception handling is not designed to deal with asynchronous situations such as network message arrivals, disk I/O completions, mouse clicks, and the like; these are best handled through other means, such as interrupt processing. * Exception handling is typically used in situations in which the error will be dealt with by a different part of the program (i.e., a different scope) from that which * detected the error. * Exceptions should not be used as an alternate mechanism for specifying flow of control. Flow of control with conventional control structures is generally clearer and more efficient than with exceptions. * Exception handling should be used to process exceptions for program components that are not geared to handling those exceptions directly. * Exception handling should be used to process exceptions from software components such as functions, libraries, and classes that are likely to be widely used, and where it does not make sense for those components to handle their own exceptions. * Exception handling should be used on large projects to handle error processing in a uniform manner for the entire project. * C++ exception handling is geared to situations in which the function that detects an error is unable to deal with it. Such a function will throw an exception. If the exception matches the type of the parameter in one of the catch blocks, the code for that catch block is executed. Otherwise, function terminate is called, which by default calls function abort. * The programmer encloses in a try block the code that may generate an error that will produce an exception. The try block is immediately followed by one or more * catch blocks. Each catch block specifies the type of exception it can catch and handle. Each catch block contains an exception handler. * Program control on a thrown exception leaves the try block and searches the catch blocks in order for an appropriate handler. If no exceptions are thrown in the try block, the exception handlers for that block are skipped and the program resumes execution after the last catch block. * Exceptions are thrown in a try block in a function or from a function called directly or indirectly from the try block. * Once an exception is thrown, control cannot return * directly to the throw point. * It is possible to communicate information to the exception handler from the point of the exception. That information is the type of thrown object or information placed into the thrown object. * A popular exception type thrown is char *. It is common to simply include an error message as the operand of the throw. * The exceptions thrown by a particular function can be specified with an exception specification. An empty exception specification states that the function will not throw any exceptions. * Exceptions are caught by the closest exception han * dler (for the try block from which the exception was thrown) specifying an appropriate type. * As part of throwing an exception, a temporary copy of the throw operand is created and initialized. This temporary object then initializes the proper variable in the exception handler. The temporary object is destroyed when the exception handler is exited. * Errors are not always checked explicitly. A try block, for example, may appear to contain no error checking and include no throw statements. But code referenced in the try block could certainly cause error-checking code in constructors to execute. * An exception terminates the block in which the * exception occurred. * Exception handlers are contained in catch blocks. Each catch block starts with the keyword catch followed by parentheses containing a type and an optional parameter name. This is followed by braces delineating the exception-handling code. When an exception is caught, the code in the catch block is executed. * The catch handler defines its own scope. * The parameter in a catch handler can be named or unnamed. If the parameter is named, the parameter can be referenced in the handler. If the parameter is unnamed, i.e., only a type is listed for the purpose of matching with the thrown object type or an ellipsis for * all types, then the handler will ignore the thrown object. The handler may rethrow the object to an outer try block. * It is possible to specify customized behavior to replace function terminate by designating another function to be executed and providing that function's name as the argument in a set_terminate function call. * catch(...) means to catch all exceptions. * It is possible that no handler will match a particular thrown object. This causes the search for a match to continue in an enclosing try block. * The exception handlers are searched in order for an appropriate match. The first handler that yields a match * is executed. When that handler finishes executing, control resumes with the first statement after the last catch block. * The order of the exception handlers affects how an exception is handled. * A derived-class object can be caught either by a handler specifying the derived-class type or by handlers specifying the types of any base classes of that derived class. * Sometimes a program may process many closely related types of exceptions. Instead of providing separate exception classes and catch handlers for each, a programmer can provide a single exception class and * catch handler for a group of exceptions. As each exception occurs, the exception object can be created with different private data. The catch handler can examine this private data to distinguish the type of the exception. * It is possible that even though a precise match is available, a match requiring standard conversions will be made because that handler appears before the one that would result in a precise match. * By default, if no handler is found for an exception, the program terminates. * An exception handler cannot directly access variables in the scope of its try block. Information the han * dler needs is normally passed in the thrown object. * Exception handlers can take a closer look at an error and decide to call terminate. They can rethrow an exception. They can convert one type of exception into another by throwing a different exception. They can perform any necessary recovery and resume execution after the last exception handler. They can look at the situation causing the error, remove the cause of the error, and retry by calling the original function that caused an exception (this would not create infinite recursion). They can simply return some status value to their environment, etc. * A handler that catches a derived-class object should * be placed before a handler that catches a base-class object. If the base-class handler were first, it would catch both the base-class objects and the object of classes derived from that base class. * When an exception is caught, it is possible that resources may have been allocated but not yet released in the try block. The catch handler should release these resources. * It is possible that the handler that catches an exception may decide that it cannot process the exception. In this case, the handler can simply rethrow the exception. A throw with no arguments rethrows the exception. If no exception was thrown to begin with, then the * rethrow causes a call to terminate. * Even if a handler can process an exception, and regardless of whether it does any processing on that exception, the handler can rethrow the exception for further processing outside the handler. A rethrown exception is detected by the next enclosing try block and is handled by an exception handler listed after that enclosing try block. * A function with no exception specification can throw any exception. * Function unexpected calls a function specified with function set_unexpected. If no function has been specified in this manner, terminate is called by default. * Function terminate can be called in various ways: explicitly; if a thrown exception cannot be caught; if the stack is corrupted during exception handling; as the default action on a call to unexpected; or if during stack unwinding initiated by an exception, an attempt by a destructor to throw an exception causes terminate to be called. * Prototypes for functions set_terminate and set_unexpected are found in header files <terminate.h> and <unexpected.h>, respectively. * Functions set_terminate and set_unexpected return pointers to the last function called, by terminate and unexpected. This enables the programmer to save the * function pointer so it can be restored later. * Functions set_terminate and set_unexpected take pointers to functions as arguments. Each argument must point to a function with void return type and no arguments. * If the last action of a user-defined termination function is not to exit a program, function abort will automatically be called to end program execution after the other statements of the user-defined termination function are executed. * An exception thrown outside a try block will cause the program to terminate. * If a handler cannot be found after a try block, stack * unwinding continues until an appropriate handler is found. If a handler is ultimately not found, then terminate is called which by default aborts the program with abort. * Exception specifications list the exceptions that may be thrown from a function. A function may throw the indicated exceptions, or it may throw derived types. If an exception not listed in the exception specification is thrown, unexpected is called. * If a function throws an exception of a particular class type, that function can also throw exceptions of all classes derived from that class with public inheritance. * To catch an exception, the exception handler must * have access to a copy constructor for the thrown object. * Exceptions thrown from constructors cause destructors to be called for all completed base-class objects and member objects of the object being constructed before the exception is thrown. * If an array of objects has been partially constructed when an exception occurs, only the destructors for the constructed array elements will be called. * Exceptions thrown from destructors can be caught by enclosing the function that calls the destructor in a try block and provide a catch handler with the proper type. * A powerful reason for using inheritance with exceptions is for creating the ability to catch a variety of * related errors easily with concise notation. One could certainly catch each type of derived-class exception object individually, but it is much more concise to simply catch the base-class exception object. * The ANSI/ISO C++ draft standard specifies that when new fails, it throws a bad_alloc exception (bad_alloc is defined in header file <new>). * Many compilers are not current with the ANSI/ISO C++ draft standard and still use the version of new that returns 0 on failure. * Function set_new_handler (prototyped in header file <new> or <new.h>) takes as its argument a function pointer for a function that takes no arguments and * returns void. The function pointer is registered as the function to call when new fails. Once a new handler is registered with set_new_handler, new will not throw bad_alloc on failure. * An object of class auto_ptr maintains a pointer to dynamically allocated memory. When an auto_ptr object goes out of scope, it automatically performs a delete operation on its pointer data member. Class template auto_ptr provides operators * and -> so an auto_ptr object can be used like a regular pointer variable. * The C++ draft standard includes a hierarchy of exception classes headed by base class exception * (defined in header file <exception>) which offers the service what() that is overridden in each derived class to issue an appropriate error message. * By including std::bad_exception in the throw list of a function definition, if an unexpected exception occurs, unexpected() will throw bad_exception instead of terminating (by default) or instead of calling another function specified with set_unexpected.
The programmer determines the order in which the exception handlers are listed. This order can affect how exceptions originating in that try block are handled. If you are getting unexpected behavior in your
program's handling of exceptions it may be because an early catch block is intercepting and handling the exceptions before they reach your intended catch handler.

Using inheritance with exceptions enables an exception handler to catch related errors with a rather concise notation. One could certainly catch each type of pointer or reference to a derived- class exception object
individually, but it is more concise to catch pointers or references to base-class exception objects instead. Also, catching pointers or references to derived- class exception objects individually is subject to error if the
programmer forgets to explicitly test for one or more of the derived- class pointer or reference types.

To catch all exceptions that might be thrown in a try block, use catch( ... ).

A
abort
assert macro
auto_ptr
B
bad_alloc exception
bad_cast
bad_typeid
C
catch all exceptions
catch an exception
catch block
catch( ... )
D
dynamic_cast
E
empty exception specification
empty throw statement
enclosing try block
exception
exception handler
exception object
exception specification
exit
F
fault tolerance
H
handler
I
invalid_argument
L
length_error
logic_error
M
<memory>
mission critical
N
nested scope
<new>
new_handler
nothrow
O
out_of_range
overflow_error
R
rethrow an exception
runtime_error
S
set_new_handler
set_terminate
set_unexpected
stack unwinding
std::bad_exception
<stdexcept>
T
terminate
throw an exception
throw expression
throw list
throw point
throw with no arguments
throw()
try block
U
under flow_error
unexpected()

Fig. 13.1 A simple exception-handling example with divide by zero.
Fig. 13.2 Rethrowing an exception.
Fig. 13.3 Demonstration of stack unwinding.
Fig. 13.4 Demonstrating new returning 0 on failure.
Fig. 13.5 Demonstrating new throwing bad_alloc on failure.
Fig. 13.6 Demonstrating set_new_handler.
Fig. 13.7 Demonstrating auto_ptr.

Exercise 13.1
List five common examples of exceptions.


Exercise 13.2
Give several reasons why exception-handling techniques should not be used for conventional program control.


Exercise 13.3
Why are exceptions appropriate for dealing with errors produced by library functions?


Exercise 13.4
What is a "resource leak?"


Exercise 13.5
If no exceptions are thrown in a try block, where does control proceed to after the try block completes execution?


Exercise 13.6
What happens if an exception is thrown outside a try block?


Exercise 13.7
Give a key advantage and a key disadvantage of using catch(...).


Exercise 13.8
What happens if no catch handler matches the type of a thrown object?


Exercise 13.9
What happens if several handlers match the type of the thrown object?


Exercise 13.10
Why would a programmer specify a base-class type as the type of a catch handler and then throw objects of derived-class types?


Exercise 13.11
How might a catch handler be written to process related types of errors without using inheritance among exception classes?


Exercise 13.12
What pointer type is used in a catch handler to catch any exception of any pointer type?


Exercise 13.13
Suppose a catch handler with a precise match to an exception object type is available. Under what circumstances might a different handler be executed for exception objects of that type?


Exercise 13.14
Must throwing an exception cause program termination?


Exercise 13.15
What happens when a catch handler throws an exception?


Exercise 13.16
What does the statement throw; do?


Exercise 13.17
How does the programmer restrict the exception types that can be thrown from a function?


Exercise 13.18
What happens if a function does throw an exception of a type not allowed by the exception specification for the function?


Exercise 13.19
What happens to the automatic objects that have been constructed in a try block when that block throws an exception?


Exercise 13.20
List the various exceptional conditions that have occurred in programs throughout this text. List as many additional exceptional conditions as you can. For each of these, describe briefly how a program would typically handle the exception using the exception-handling techniques discussed in this chapter. Some typical exceptions are: Division by zero, arithmetic overflow, array subscript out of bounds, exhaustion of the free store, etc.


Exercise 13.21
Under what circumstances would the programmer not provide a parameter name when defining the type of the object that will be caught by a handler?

Exercise 13.22
A program contains the statement

throw;

Where would you normally expect to find such a statement? What if that statement appeared in a different part of a program?

Exercise 13.23
Under what circumstances would you use the following statement?

catch(...) { throw; }



Exercise 13.24
Compare and contrast exception handling with the various other error- processing schemes discussed in the text.


Exercise 13.25
List the benefits of exception handling over conventional means of error processing.


Exercise 13.26
Provide reasons why exceptions should not be used as an alternate form of program control.


Exercise 13.27
Describe a technique for handling related exceptions.

Exercise 13.28
Until this chapter, we have found that dealing with errors detected by constructors is a bit awkward. Exception handling gives us a much better means of dealing with such errors. Consider a constructor for a String class. The constructor uses new to obtain space from the free store. Suppose new fails. Show how you would deal with this without exception handling. Discuss the key issues. Show how you would deal with such memory exhaustion with exception handling. Explain why the exception handling method is superior.


Exercise 13.29
Suppose a program throws an exception and the appropriate exception handler begins executing. Now suppose that the exception handler itself throws the same exception. Does this create an infinite recursion? Write a C++ program to check your observation.


Exercise 13.30
Use inheritance to create a base exception class and various derived exception classes. Then show that a catch handler specifying the base class can catch derived-class exceptions.


Exercise 13.31
Show a conditional expression that returns either a double or an int. Provide an int catch handler and a double catch handler. Show that only the double catch handler executes regardless of whether the int or the double is returned.


Exercise 13.32
Write a C++ program designed to generate and handle a memory exhaustion error. Your program should loop on a request to create dynamic storage through operator new.


Exercise 13.33
Write a C++ program which shows that all destructors for objects constructed in a block are called before an exception is thrown from that block.


Exercise 13.34
Write a C++ program which shows that member object destructors are called for only those member objects that were constructed before an exception occurred.


Exercise 13.35
Write a C++ program that demonstrates how any exception is caught with catch(...).


Exercise 13.36
Write a C++ program which shows that the order of exception handlers is important. The first matching handler is the one that executes. Compile and run your program two different ways to show that two different handlers execute with two different effects.


Exercise 13.37
Write a C++ program that shows a constructor passing information about constructor failure to an exception handler after a try block.


Exercise 13.38
Write a C++ program that uses a multiple inheritance hierarchy of exception classes to create a situation in which the order of exception handlers matters.


Exercise 13.39
Using setjmp/longjmp, a program can transfer control immediately to an error routine from a deeply nested function invocation. Unfortunately, as the stack is unwound, destructors are not called for the automatic objects that were created during the sequence of nested function calls. Write a C++ program which demonstrates that these destructors are, in fact, not called.


Exercise 13.40
Write a C++ program that illustrates rethrowing an exception.


Exercise 13.41
Write a C++ program that uses set_unexpected to set a user-defined function for unexpected, uses set_unexpected again, and then resets unexpected back to its previous function. Write a similar program to test set_terminate and terminate.


Exercise 13.42
Write a C++ program which shows that a function with its own try block does not have to catch every possible error generated within the try. Some exceptions can slip through to, and be handled in, outer scopes.


Exercise 13.43
Write a C++ program that throws an error from a deeply nested function call and still has the catch handler following the try block enclosing the call chain catch the exception.


* To use try, throw and catch to watch for, indicate and handle exceptions, respectively. * To process uncaught and unexpected exceptions. * To be able to process new failures. * To use auto_ptr to prevent memory leaks. * To understand the standard exception hierarchy.
Avoid using exception handling for purposes other than error handling because this can reduce program clarity.

Use exceptions for errors that must be processed in a different scope from where they occur. Use other means of error handling for errors that will be processed in the scope in which they occur.

Use conventional error- handling techniques rather than exception handling for straightforward, local error processing in which a program is easily able to deal with its own errors.

Associating each type of execution-time error with an appropriately named exception object improves program clarity.

Exception handling is generally implemented in compilers in such a manner that when an exception does not occur, little or no overhead is imposed by the presence of exception-handling code. When exceptions
happen, they do incur execution-time overhead. Certainly the presence of exception- handling code makes the program consume more memory.

Although it is possible to use exception handling for purposes other than error handling, this can reduce program performance.

Figure 13.1 A simple exception-handling example with divide by zero.



Figure 13.2 Rethrowing an exception.



Figure 13.3 Demonstration of stack unwinding.



Figure 13.4 Demonstrating new returning 0 on failure.



Figure 13.5 Demonstrating new throwing bad_alloc on failure.



Figure 13.6 Demonstrating set_new_handler.



Figure 13.7 Demonstrating auto_ptr.



Flow of control with conventional control structures is generally clearer and more efficient than with exceptions.

Exception handling is well suited to systems of separately developed components. Exception handling makes it easier to combine the components. Each component can perform its own exception detection separate from
the handling of the exceptions in another scope.

When dealing with libraries, the caller of the library function will likely have unique error processing in mind for an exception generated in the library function. It is unlikely that a library function will perform error
processing that would meet the unique needs of all users. Therefore, exceptions are an appropriate means for dealing with errors produced by library functions.

A key to exception handling is that the portion of a program or system that will handle the exception can be quite different or distant from the portion of the program that detected and generated
the exceptional situation.

If it is necessary to pass information about the error that caused an exception, such information can be placed in the thrown object. The catch handler would then contain a parameter name through which
that information could be referenced.

An object can be thrown without containing information to be passed; in this case, mere knowledge that an exception of this type has been thrown may provide sufficient information for the
handler to do its job correctly.

A weakness with catching exceptions with catch(...) is that you normally cannot be sure what the exception type is. Another weakness is that without a named parameter, there is no way to refer to the
exception object inside the exception handler.

Another reason not to use exceptions for conventional flow of control is that these "additional" exceptions can get in the way of genuine error-type exceptions. It becomes more difficult for the programmer to keep
track of the number of exception cases. For example, when a program processes an excessive variety of exceptions, can we really be sure of just what is being caught by a catch(...)? Exceptional situations
should be rare, not commonplace.

It is best to incorporate your exception- handling strategy into a system from the inception of the design process. It is difficult to add effective exception handling after a system has been implemented.

Use catch(...) to perform recovery that does not depend on the type of the exception, such as releasing common resources. The exception can be rethrown to alert more specific enclosing catch blocks.
The ANSI/ISO C++ draft standard recommends that to make programs more robust, programmers should use the version of new that throws bad_alloc exceptions on failure.

The standard exception hierarchy is meant to serve as a starting point. Users can throw standard exceptions, throw exceptions derived from the standard exceptions or throw their own exceptions not derived
from the standard exceptions.

Another reason exceptions can be dangerous as an alternative to normal flow of control is that the stack is unwound and resources allocated prior to the occurrence of the exception may not be freed. This
problem can be avoided by careful programming.

Aborting a program could leave a resource in a state in which other programs would not be able to acquire the resource, hence the program would have a so-called "resource leak."

It is possible to throw a conditional expression. But be careful because promotion rules may cause the value returned by the conditional expression to be of a different type than you may expect. For example, when
throwing an int or a double from the same conditional expression, the conditional expression will convert the int to a double. Therefore the result will always be caught by a catch with a double argument rather
than sometimes catching double (for the actual double) and sometimes catching int.

Exceptions should be thrown only within a try block. An exception thrown outside a try block causes a call to terminate.

Specifying a comma- separated list of catch arguments is a syntax error.

Placing catch(...) before other catch blocks would prevent those blocks from ever being executed; catch(...) should always be placed last in the list of handlers following a try block.

Placing a catch that catches a base class object before a catch that catches an object of a class derived from that base class is a logic error. The base-class catch will catch all objects of classes derived from that base
class, so the derived class catch would never be executed.

Placing an exception handler with a void * argument type before exception handlers with other pointer types causes a logic error. The void * handler would catch all exceptions of pointer types, so the other
handlers would never execute.

Placing a semicolon after a try block or after any catch handler (other than the last catch) following a try block is a syntax error.

Assuming that after an exception is processed, control will return to the first statement after the throw is a logic error

Assuming that an exception thrown from a catch handler will be processed by that handler or any other handler associated with the try block that threw the exception which caused the original
catch handler to execute is a logic error.

Placing an empty throw statement outside a catch handler; executing such a throw causes a call to terminate.

Throwing an exception not in a function's exception specification causes a call to unexpected.

User-defined exception classes need not necessarily be derived from class exception. Thus, writing catch( exception e ) is not guaranteed to catch all exceptions a program may encounter.

This chapter does not contain any Portability tips.
This chapter does not contain any Applet Examples.